package cn.my.springHDFS.service;

import cn.my.springHDFS.Interceptor.AsyncTaskServiceImpl;
import cn.my.springHDFS.domain.Plupload;
import cn.my.springHDFS.domain.SpiderStatus;
import cn.my.springHDFS.util.HDFS_Util;
import cn.my.springHDFS.util.PropertyUtil;
import cn.my.springHDFS.util.ToolUtil;
import org.apache.hadoop.conf.Configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import java.io.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Created by Leo on 2017/6/25.
 * Plupload Service模块，同Plupload实体类一样，因为要用到Spring web相关依赖，所以不将其放在Service模块
 */
public class PluploadServiceImpl {

    /**
     * 上传方法一
     * @param plupload
     * @param pluploadDir
     */
    public static Boolean upload(Plupload plupload, File pluploadDir, Authentication authentication){
        String fileName = "" + System.currentTimeMillis() + "-" + plupload.getName();//在服务器内生成唯一文件名
        String uploadPath = upload(plupload, pluploadDir, fileName);
        System.out.println("uploadPath: " + uploadPath);
        System.out.println("FileName: " + fileName);
        String uploadToHDFS_path = plupload.getUserUploadPath();
        List<GrantedAuthority> grantedAuthorityList = (List<GrantedAuthority>) authentication.getAuthorities();
        String auth = grantedAuthorityList.get(0).toString();
        System.out.println(auth);
        if (auth.equals("ROLE_USER")) {
            System.out.println(authentication.getPrincipal().toString());
            uploadToHDFS_path = "/" + authentication.getPrincipal().toString();
        }
        System.out.println(uploadToHDFS_path);
        if (uploadPath != null){
            String regex = "[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}:[0-9]{1,4}";
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(plupload.getUserUploadPath());
            if (auth.equals("ROLE_SUPER") || auth.equals("ROLE_SECONDADMIN")){
                return localFileToHDFS(uploadPath, matcher.replaceAll(""), authentication.getPrincipal().toString());
            } else {
                return localFileToHDFS(uploadPath, matcher.replaceAll(""), authentication.getPrincipal().toString());
            }
        }
        //异步任务
//        AsyncTaskServiceImpl asyncTaskService = new AsyncTaskServiceImpl();
//        asyncTaskService.uploadUserFileToHDFS(uploadPath, uploadToHDFS_path);
//        System.out.println("Async结束");

        return false;
    }

    /**
     * 上传方法二
     * @param plupload
     * @param pluploadDir
     * @param fileName
     */
    private static String upload(Plupload plupload,File pluploadDir,String fileName){

        int chunks = plupload.getChunks();//用户上传文件被分隔的总块数
        int nowChunk = plupload.getChunk();//当前块，从0开始

        //这里Request请求类型的强制转换可能出错，配置文件中向SpringIOC容器引入multipartResolver对象即可。
        MultipartHttpServletRequest multipartHttpServletRequest  = (MultipartHttpServletRequest)plupload.getRequest();

        //调试发现map中只有一个键值对
        MultiValueMap map = multipartHttpServletRequest.getMultiFileMap();

        //最终文件路径
        String finalFilePath = null;

        if(map!=null){
            try{
                Iterator iterator = map.keySet().iterator();
                while(iterator.hasNext()){

                    String key = (String) iterator.next();
                    List<MultipartFile> multipartFileList = (List<MultipartFile>) map.get(key);

                    for(MultipartFile multipartFile : multipartFileList){//循环只进行一次

                        plupload.setMultipartFile(multipartFile);//手动向Plupload对象传入MultipartFile属性值
                        File targetFile = new File(pluploadDir+"/"+fileName);//新建目标文件，只有被流写入时才会真正存在
                        finalFilePath = targetFile.toString();
                        if(chunks>1){//用户上传资料总块数大于1，要进行合并

                            File tempFile = new File(pluploadDir.getPath()+"/"+multipartFile.getName());
                            //第一块直接从头写入，不用从末端写入
                            savePluploadFile(multipartFile.getInputStream(), tempFile, nowChunk == 0 ? false : true);

                            if(chunks-nowChunk==1){//全部块已经上传完毕，此时targetFile因为有被流写入而存在，要改文件名字
                                tempFile.renameTo(targetFile);
                            }
                        }
                        else{
                            //只有一块，就直接拷贝文件内容
                            multipartFile.transferTo(targetFile);
                        }
                    }
                }
                return finalFilePath;
            }
            catch (IOException e){
                e.printStackTrace();
                return null;
            }
        }
        return null;
    }

    /**
     * 断点续传
     * @param inputStream
     * @param tempFile
     * @param flag
     */
    private static void savePluploadFile(InputStream inputStream,File tempFile,boolean flag){
        OutputStream outputStream = null;
        try {
            if(flag==false){
                //从头写入
                outputStream = new BufferedOutputStream(new FileOutputStream(tempFile));
            }
            else{
                //从末端写入
                outputStream = new BufferedOutputStream(new FileOutputStream(tempFile,true));
            }
            byte[] bytes = new byte[1024];
            int len = 0;
            while ((len = (inputStream.read(bytes)))>0){
                outputStream.write(bytes,0,len);
            }
        }
        catch (FileNotFoundException e){
            e.printStackTrace();
        }
        catch (IOException e){
            e.printStackTrace();
        }
        finally {
            try{
                outputStream.close();
                inputStream.close();
            }
            catch (IOException e){
                e.printStackTrace();
            }
        }
    }

    /**
     * 本地文件上传到HDFS
     * @param localFilePath
     * @param hdfsPath
     * @return
     */
    private static boolean localFileToHDFS(String localFilePath, String hdfsPath, String username){
        HDFS_Util hdfs_util = new HDFS_Util();

        //实例化ToolUtil和Hadoop Configuration对象
        ToolUtil toolUtil = new ToolUtil();
        List<Object> objectList = toolUtil.ipSwitch(SpiderStatus.getIsActive());
        Configuration conf = (Configuration) objectList.get(0);

        //本地服务器文件上传到HDFS
        return hdfs_util.putToHDFS(localFilePath, hdfsPath, conf, username);
    }

}
